package com.zan.zanchat.common.user.service.impl;

import cn.hutool.core.thread.NamedThreadFactory;
import cn.hutool.http.HttpUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.zan.zanchat.common.common.domain.vo.resp.ApiResult;
import com.zan.zanchat.common.common.utils.JsonUtils;
import com.zan.zanchat.common.user.dao.UserDao;
import com.zan.zanchat.common.user.domain.entity.IpDetail;
import com.zan.zanchat.common.user.domain.entity.IpInfo;
import com.zan.zanchat.common.user.domain.entity.User;
import com.zan.zanchat.common.user.service.IpService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Author Zan
 * @Create 2024/3/17 20:19
 * @Description :
 */
@Service
@Slf4j
public class IpServiceImpl implements IpService, DisposableBean {

    /**
     * 搭建线程池
     */
    private static ExecutorService executor = new ThreadPoolExecutor(
            1,
            1,
            0L,
            TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(500),
            new NamedThreadFactory("refresh-ipDetail", false));

    @Resource
    private UserDao userDao;

    @Override
    public void refreshIpDetailAsync(Long uid) {
        executor.execute(() -> {
            // 找到登录用户
            User user = userDao.getById(uid);
            if (Objects.isNull(user)) {
                return;
            }
            // 获取用户原先的ip信息
            IpInfo ipInfo = user.getIpInfo();
            // 判断是否需要更换ip（若ip一样，不需要刷新，不一样，则更新）
            String ip = ipInfo.needRefreshIp();
            if (StringUtils.isBlank(ip)) {
                return;
            }
            // 进行ip的刷新 - 新的ipDetail信息
            IpDetail ipDetail = tryGetIpDetailOrNullThreeTimes(ip);
            if (Objects.nonNull(ipDetail)) {
                // 拿到了ip - 进行刷新，保存ip，传入新的ipDetail信息
                ipInfo.refreshIpDetail(ipDetail);
                User updateUser = new User();
                updateUser.setId(uid);
                updateUser.setIpInfo(ipInfo);
                userDao.updateById(updateUser);
            }
        });
    }

    /**
     * 尝试3次进行IP解析
     * 1次解析不成功，休眠2s进行等待
     * 继续尝试解析，3次解析不成功为失败
     */
    private static IpDetail tryGetIpDetailOrNullThreeTimes(String ip) {
        for (int i = 0; i < 3; i++) {
            IpDetail ipDetail = getIpDetailOrNull(ip);
            if (Objects.nonNull(ipDetail)) {
                return ipDetail;
            }
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                log.error("tryGetIpDetailOrNullThreeTimes InterruptedException", e);
            }
        }
        return null;
    }

    /**
     * 发起GET请求调用淘宝的ip解析API
     */
    private static IpDetail getIpDetailOrNull(String ip) {
        try {
            String url = "https://ip.taobao.com/outGetIpInfo?ip=" + ip + "&accessKey=alibaba-inc";
            String data = HttpUtil.get(url);
            // 进行反序列化 - 这里由于用到了TypeReference，相当于是判断类型来进行匹配
            ApiResult<IpDetail> ipDetailApiResult = JsonUtils.toObj(data, new TypeReference<ApiResult<IpDetail>>() {});
            return ipDetailApiResult.getData(); // 获取json值
        } catch (Exception e) {
            return null;
        }
    }

    public static void main(String[] args) {
        Date begin = new Date();
        for (int i = 0; i < 100; i++) {
            int finalI = i;
            executor.execute(() -> {
                IpDetail ipDetail = tryGetIpDetailOrNullThreeTimes("112.96.166.230");
                if (Objects.nonNull(ipDetail)) {
                    Date date = new Date();
                    System.out.println(String.format("第%d次成功，目前耗时：%d", finalI, (date.getTime() - begin.getTime())));
                }
            });
        }
    }

    /**
     * 优雅停机，关闭线程池
     */
    @Override
    public void destroy() throws Exception {
        executor.shutdown(); // 线程池不会在执行任务了
        if (!executor.awaitTermination(30, TimeUnit.SECONDS)) { // 最多等30秒，处理不完就拉倒
            if (log.isErrorEnabled()) {
                log.error("Timed out while waiting for executor [{}] to terminate", executor);
            }
        }
    }
}
